package pl.shenlon.poker.gui;

import java.awt.Container;
import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableModel;

import net.miginfocom.swing.MigLayout;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import pl.shenlon.ga.Population;
import pl.shenlon.ga.Specimen;
import pl.shenlon.persistence.PopulationPersistance;
import pl.shenlon.poker.game.gametypes.PokerGame;
import pl.shenlon.poker.gui.testhands.TestHandsManagerDialog;

public class AppFrame extends JFrame {
	private AboutDialog aboutDialog, helpDialog;
	private JButton calculateButton;
	private PopulationConfigPanel configPanel;
	private JFileChooser fileChooser;
	private GameDialog gameDialog;
	private Population<?> population;
	private JProgressBar progressBar;
	private JTable resultTable;
	private DefaultTableModel resultTableModel;
	private JScrollPane resultTableScrollPane;
	private JMenuItem saveFileMenuItem;
	private SpecimenDataViewDialog specimenDataViewDialog;
	private TestHandsManagerDialog testHandsManagerDialog;

	public AppFrame() {
		initComponent();
		pack();
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent event) {
				int choice = JOptionPane.showConfirmDialog(AppFrame.this,
						"Czy na pewno chcesz wyłączyć aplikację?",
						"Potwierdzenie", JOptionPane.OK_CANCEL_OPTION,
						JOptionPane.QUESTION_MESSAGE);
				if (choice == JOptionPane.YES_OPTION) {
					System.exit(0);
				}
			}
		});
		setIconImage(Toolkit.getDefaultToolkit().getImage(
				getClass().getResource("/data/icon.png")));
		setTitle("Genetyczny Poker - Daniel Cisek");
	}
	private void calculate() {
		SwingWorker<Map<Integer, Long>, Integer> swingWorker = new SwingWorker<Map<Integer, Long>, Integer>() {

			@Override
			protected Map<Integer, Long> doInBackground() throws Exception {
				Map<Integer, Long> executionTimes = new HashMap<Integer, Long>();
				calculateButton.setEnabled(false);
				progressBar.setValue(0);
				setPopulation(configPanel.getPopulation());
				int generationsCount = configPanel.getGenerationsCount();
				for (int i = 0; i < generationsCount; i++) {
					long start = System.currentTimeMillis();
					population.breed();
					executionTimes.put(i, System.currentTimeMillis() - start);
					int progress = (int) ((double) i / generationsCount * 100);
					fillTableWithSpecimens(population.getPopulation());
					publish(progress);
				}
				population.evaluatePopulation();
				population.sortDescending();
				publish(100);
				return executionTimes;
			}

			@Override
			public void done() {
				calculateButton.setEnabled(true);
				fillTableWithSpecimens(population.getPopulation());
			}

			@Override
			public void process(List<Integer> chunks) {
				progressBar.setValue(chunks.get(0));
				setProgress(chunks.get(0));
			}
		};
		swingWorker.execute();
	}

	private void fillTableWithSpecimens(Specimen... specimens) {
		resultTableModel.setRowCount(0);
		int i = 1;
		for (Specimen specimen : specimens) {
			Object[] rowData = new Object[]{
					i++,
					specimen.getScore(),
					"Szczegóły",
					"Zagraj",};
			resultTableModel.addRow(rowData);
		}
	}

	public JButton getCalculateButton() {
		return calculateButton;
	}
	public PopulationConfigPanel getConfigPanel() {
		return configPanel;
	}
	public Population<?> getPopulation() {
		return population;
	}
	public JProgressBar getProgressBar() {
		return progressBar;
	}
	public JTable getResultTable() {
		return resultTable;
	}
	public DefaultTableModel getResultTableModel() {
		return resultTableModel;
	}
	public JScrollPane getResultTableScrollPane() {
		return resultTableScrollPane;
	}
	private void initComponent() {
		Container contentPane = getContentPane();
		contentPane.setLayout(new MigLayout());

		setJMenuBar(initializeMenu());
		configPanel = new PopulationConfigPanel();
		contentPane.add(configPanel, "wrap");

		calculateButton = new JButton("Wyznacz");
		calculateButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					calculate();
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		});

		contentPane.add(calculateButton, "growx, wrap");

		progressBar = new JProgressBar(0, 100);
		progressBar.setStringPainted(true);
		contentPane.add(progressBar, "growx, wrap");

		resultTableModel = new DefaultTableModel(0, 4) {
			/**
			 * 
			 */
			private static final long serialVersionUID = -2158181157394598431L;

			@Override
			public boolean isCellEditable(int row, int col) {
				if (col == 2 || col == 3) {
					return true;
				}
				return false;
			}
		};
		resultTableModel.setColumnIdentifiers(new Object[]{
				"ID",
				"WYNIK",
				"SZCZEGÓŁY",
				"GRA"});

		resultTable = new JTable(resultTableModel);
		resultTable.setFillsViewportHeight(true);
		resultTableScrollPane = new JScrollPane(resultTable);

		new ButtonColumn(resultTable, new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				int index = Integer.valueOf(e.getActionCommand());
				if (specimenDataViewDialog == null) {
					specimenDataViewDialog = new SpecimenDataViewDialog();
				}
				specimenDataViewDialog.setSpecimen(population
						.getSpecimen(index));
				specimenDataViewDialog.setVisible(true);
			}
		}, 2);

		new ButtonColumn(resultTable, new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				if (gameDialog == null) {
					gameDialog = new GameDialog();
				}
				PokerGame game = null;
				try {
					game = population.getEvaluator().getPokerGameClass()
							.newInstance();
				} catch (InstantiationException | IllegalAccessException ex) {
					JOptionPane.showMessageDialog(AppFrame.this,
							"Wystąpił problem w trakcie uruchamiania gry.",
							"Błąd", JOptionPane.ERROR_MESSAGE);
				}
				gameDialog.newGame(population.getCash(), population
						.getSpecimen(Integer.valueOf(e.getActionCommand())),
						game);

			}
		}, 3);
		add(resultTableScrollPane, "h 200, grow, push, wrap");
	}
	private JMenuBar initializeMenu() {
		JMenuBar menuBar = new JMenuBar();
		JMenu fileMenu = new JMenu("Plik"), configMenu = new JMenu("Opcje"), helpMenu = new JMenu(
				"Pomoc");

		JMenuItem loadFileMenuItem = new JMenuItem("Wczytaj populację");
		loadFileMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl O"));
		loadFileMenuItem.setMnemonic('O');

		saveFileMenuItem = new JMenuItem("Zapisz populację");
		saveFileMenuItem.setEnabled(false);
		saveFileMenuItem.setAccelerator(KeyStroke.getKeyStroke("ctrl S"));
		saveFileMenuItem.setMnemonic('Z');
		saveFileMenuItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (fileChooser == null) {
					fileChooser = new JFileChooser();
					fileChooser.setAcceptAllFileFilterUsed(false);
					fileChooser.setCurrentDirectory(new File("."));
					fileChooser.setFileFilter(new PopulationFileFilter());

				}
				int choice = fileChooser.showSaveDialog(AppFrame.this);
				if (choice == JFileChooser.APPROVE_OPTION) {
					File selectedFile = fileChooser.getSelectedFile();
					if (!selectedFile.getName().endsWith(
							PopulationFileFilter.POPULATION_FILE_EXTENSION)) {
						selectedFile = new File(
								selectedFile.getPath()
										+ "."
										+ PopulationFileFilter.POPULATION_FILE_EXTENSION);
					}

					Element persistedPopulation = PopulationPersistance
							.persist(population);
					Document document = new Document(persistedPopulation);
					XMLOutputter outputter = new XMLOutputter(Format
							.getPrettyFormat());
					try {
						BufferedWriter writer = new BufferedWriter(
								new FileWriter(selectedFile));
						outputter.output(document, writer);
					} catch (IOException e) {
						JOptionPane
								.showMessageDialog(
										AppFrame.this,
										"Wystąpił błąd w trakcie zapisu populacji do pliku. Spróbuj ponownie później.",
										"Błąd", JOptionPane.ERROR_MESSAGE);
					}
				}

			}
		});
		loadFileMenuItem.addActionListener(new ActionListener() {
			@SuppressWarnings("rawtypes")
			@Override
			public void actionPerformed(ActionEvent event) {

				if (fileChooser == null) {
					fileChooser = new JFileChooser();
					fileChooser.setFileFilter(new PopulationFileFilter());
					fileChooser.setAcceptAllFileFilterUsed(false);
					fileChooser.setCurrentDirectory(new File("."));
				}
				int choice = fileChooser.showSaveDialog(AppFrame.this);
				if (choice == JFileChooser.APPROVE_OPTION) {
					new SwingWorker<Population, Void>() {
						@Override
						public Population doInBackground() {
							setCursor(Cursor
									.getPredefinedCursor(Cursor.WAIT_CURSOR));
							File xmlFile = fileChooser.getSelectedFile();
							SAXBuilder builder = new SAXBuilder();
							try {
								Document document = builder.build(xmlFile);
								Population population = PopulationPersistance
										.restorePopulation(document
												.getRootElement());
								return population;
							} catch (JDOMException | IOException e) {
								JOptionPane
										.showMessageDialog(
												AppFrame.this,
												"Wystąpił błąd w trakcie wczytywania populacji.",
												"Błąd",
												JOptionPane.WARNING_MESSAGE);
							}

							return null;
						}
						@Override
						public void done() {
							setCursor(Cursor.getDefaultCursor());
							try {
								Population population = get();
								if (population != null) {
									// TODO
									setPopulation(population);
									fillTableWithSpecimens(population
											.getPopulation());
								}
							} catch (InterruptedException | ExecutionException e) {
								e.printStackTrace();
							}
						}
					}.execute();
				}
			}
		});
		fileMenu.add(saveFileMenuItem);
		fileMenu.add(loadFileMenuItem);
		fileMenu.setMnemonic('p');

		JMenuItem testHandsConfigMenuItem = new JMenuItem(
				"Zarządzanie rękami testowymi");
		testHandsConfigMenuItem.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				if (testHandsManagerDialog == null) {
					testHandsManagerDialog = new TestHandsManagerDialog();
				}
				testHandsManagerDialog.setVisible(true);
			}
		});
		testHandsConfigMenuItem
				.setAccelerator(KeyStroke.getKeyStroke("ctrl T"));
		testHandsConfigMenuItem.setMnemonic('t');

		configMenu.add(testHandsConfigMenuItem);
		configMenu.setMnemonic('o');

		menuBar.add(fileMenu);
		menuBar.add(configMenu);

		helpMenu = new JMenu("Pomoc");
		helpMenu.setMnemonic('m');
		JMenuItem aboutHelpMenuItem = new JMenuItem("O programie");
		aboutHelpMenuItem.setAccelerator(KeyStroke.getKeyStroke("F2"));
		aboutHelpMenuItem.setMnemonic('o');
		aboutHelpMenuItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent event) {
				if (aboutDialog == null) {
					aboutDialog = new AboutDialog();
					aboutDialog.setContent(getClass().getResourceAsStream(
							"/data/about.html"));
					aboutDialog.setTitle("O programie");
				}
				aboutDialog.setVisible(true);

			}
		});
		JMenuItem helpHelpMenuItem = new JMenuItem("Pomoc");
		helpHelpMenuItem.setAccelerator(KeyStroke.getKeyStroke("F1"));
		helpHelpMenuItem.setMnemonic('p');
		helpHelpMenuItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent event) {
				if (helpDialog == null) {
					helpDialog = new AboutDialog();
					helpDialog.setContent(getClass().getResourceAsStream(
							"/data/help.html"));
					helpDialog.setTitle("Pomoc");
				}
				helpDialog.setVisible(true);

			}
		});
		helpMenu.add(helpHelpMenuItem);
		helpMenu.add(aboutHelpMenuItem);
		menuBar.add(helpMenu);
		return menuBar;
	}
	public void setCalculateButton(JButton calculateButton) {
		this.calculateButton = calculateButton;
	}
	public void setConfigPanel(PopulationConfigPanel configPanel) {
		this.configPanel = configPanel;
	}
	public void setPopulation(Population<?> population) {
		this.population = population;
		saveFileMenuItem.setEnabled(true);
		fillTableWithSpecimens(population.getPopulation());
		configPanel.setPopulation(population);
	}
	public void setProgressBar(JProgressBar progressBar) {
		this.progressBar = progressBar;
	}
	public void setResultTable(JTable resultTable) {
		this.resultTable = resultTable;
	}
	public void setResultTableModel(DefaultTableModel resultTableModel) {
		this.resultTableModel = resultTableModel;
	}

	public void setResultTableScrollPane(JScrollPane resultTableScrollPane) {
		this.resultTableScrollPane = resultTableScrollPane;
	}
}